package com.ruoyi.biz.service.impl;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.biz.domain.BizSimilarityResult;
import com.ruoyi.biz.domain.BizSubmitWork;
import com.ruoyi.biz.domain.MatchFragment;
import com.ruoyi.biz.mapper.BizSimilarityResultMapper;
import com.ruoyi.biz.mapper.BizSubmitWorkMapper;
import com.ruoyi.biz.mapper.FrontUserMapper;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import org.apache.poi.xwpf.usermodel.*;
import org.springframework.stereotype.Service;
import com.ruoyi.biz.mapper.BizWorkMapper;
import com.ruoyi.biz.domain.BizWork;
import com.ruoyi.biz.service.IBizWorkService;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

/**
 * 作业Service业务层处理
 * 
 * @author lhb
 * @date 2025-02-24
 */
@Service
public class BizWorkServiceImpl implements IBizWorkService 
{
    @Resource
    private BizWorkMapper bizWorkMapper;

    @Resource
    private BizSubmitWorkMapper bizSubmitWorkMapper;

    @Resource
    private BizSimilarityResultMapper similarityResultMapper;

    @Resource
    private FrontUserMapper frontUserMapper;

    /*
    * 查重
    * */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean checkWork(Long[] ids,Long workId) throws IOException {
        // 1.根据ids查询出要进行查重的作业 (未查重过的)
        List<BizSubmitWork> bizWorks = bizSubmitWorkMapper.selectByIdArr(ids);
        // 根据作业id查询出所有已经提交的作业
        List<BizSubmitWork> bizSubmitWorks = bizSubmitWorkMapper.selectAllByWorkId(workId);

        // 2.循环遍历所有作业，两两比较返回查重结果
        for (int i = 0; i < bizWorks.size(); i++) {
            BitSet bitSet = new BitSet();
            for (int j = 0; j < bizSubmitWorks.size(); j++) {
                BizSubmitWork work1 = bizWorks.get(i);
                BizSubmitWork work2 = bizSubmitWorks.get(j);
                // 如果id相同则跳过本次循环
                if (work1.getId().equals(work2.getId())) {
                    continue;
                }
                // 获取作业文件
                File file1 = new File(RuoYiConfig.getProfile() + work1.getAttachments().substring("/profile".length()));
                File file2 = new File(RuoYiConfig.getProfile() + work2.getAttachments().substring("/profile".length()));
                // 计算相似度获取返回结果并整理
                BizSimilarityResult result = compareDocuments(file1, file2,bitSet);
                result.setOriginSubmitId(work1.getId());
                result.setOriginName(frontUserMapper.selectFrontUserById(work1.getStudentId()).getUsername());
                result.setCompareSubmitId(work2.getId());
                result.setCompareName(frontUserMapper.selectFrontUserById(work2.getStudentId()).getUsername());
                result.setCreateTime(DateUtils.getNowDate());
                similarityResultMapper.insertBizSimilarityResult(result);

            }
            // 获取当前作业对于整个提交作业列表的查重率
            int cardinality = bitSet.cardinality(); // 获取bitSet中1的个数(表示当前作业与其他作业相似的字数)
            int length = cleanText(readDocxContent(new File(RuoYiConfig.getProfile() + bizWorks.get(i).getAttachments().substring("/profile".length())))).length();
            double duplicateCheckingRate = (double) cardinality / length;
            // 修改提交作业的查重率
            BizSubmitWork bizSubmitWork = new BizSubmitWork();
            bizSubmitWork.setId(bizWorks.get(i).getId());
            bizSubmitWork.setDuplicateCheckingRate(new BigDecimal(duplicateCheckingRate).setScale(2, RoundingMode.HALF_UP));
            bizSubmitWork.setReviewState(1);
            bizSubmitWorkMapper.updateBizSubmitWork(bizSubmitWork);
        }
        return true;
    }

    // 滑动窗口大小（按字符计算）
    private static final int WINDOW_SIZE = 10;
    // 最小重复片段长度
    private static final int MIN_MATCH_LENGTH = 15;

    public  BizSimilarityResult compareDocuments(File file1, File file2,BitSet bitSet) throws IOException {
        String text1 = readDocxContent(file1);
        String text2 = readDocxContent(file2);

        // 预处理文本
        String cleanText1 = cleanText(text1);
        String cleanText2 = cleanText(text2);

        // 生成文本指纹
        Map<String, List<Integer>> fingerprints1 = generateFingerprints(cleanText1);
        Map<String, List<Integer>> fingerprints2 = generateFingerprints(cleanText2);

        // 查找匹配片段
        List<MatchFragment> matches = findMatches(cleanText1, cleanText2, fingerprints1, fingerprints2);

        // 将当前相似的片段在bitSet中标记为1
        matches.forEach(match -> bitSet.set(match.getStart1(),match.getEnd1()));

        // 计算相似度（计算的是当前作业与每个作业的查重率）
        double similarity = calculateSimilarity(cleanText1, cleanText2, matches);
        BizSimilarityResult result = new BizSimilarityResult();
        result.setSimilarityScore(new BigDecimal(similarity).setScale(2, RoundingMode.HALF_UP));
        // 将List集合转换为JSON字符串便于存储在数据库中
        ObjectMapper objectMapper = new ObjectMapper();
        String detailResult = objectMapper.writeValueAsString(matches);
        result.setDetailResult(detailResult);

        return result;
    }

    // 文档读取方法
    private  String readDocxContent(File file) throws IOException {
        StringBuilder content = new StringBuilder();
        try (FileInputStream fis = new FileInputStream(file);
             XWPFDocument document = new XWPFDocument(fis)) {

            // 读取段落文本
            for (XWPFParagraph paragraph : document.getParagraphs()) {
                content.append(paragraph.getText()).append(" ");
            }

            // 读取表格文本
            for (XWPFTable table : document.getTables()) {
                for (XWPFTableRow row : table.getRows()) {
                    for (XWPFTableCell cell : row.getTableCells()) {
                        content.append(cell.getText()).append(" ");
                    }
                }
            }
        }
        return content.toString();
    }

    // 文本清洗方法（支持中文）
    private  String cleanText(String text) {
        // 匹配所有Unicode标点符号和符号字符
        String cleaned = text.toLowerCase()
                .replaceAll("[\\p{P}\\p{S}]", "") // 保留中文、字母、数字
                .replaceAll("\\s+", "");          // 合并空白
        return cleaned;
    }

    // 生成滑动窗口指纹
    private  Map<String, List<Integer>> generateFingerprints(String text) {
        Map<String, List<Integer>> fingerprints = new HashMap<>();
        for (int i = 0; i <= text.length() - WINDOW_SIZE; i++) {
            String window = text.substring(i, i + WINDOW_SIZE);
            fingerprints.computeIfAbsent(window, k -> new ArrayList<>()).add(i);
        }
        return fingerprints;
    }

    // 查找匹配片段
    private  List<MatchFragment> findMatches(String text1, String text2,
                                                                           Map<String, List<Integer>> fp1,
                                                                           Map<String, List<Integer>> fp2) {
        List<MatchFragment> matches = new ArrayList<>();

        // 遍历所有公共指纹
        fp1.keySet().retainAll(fp2.keySet());
        for (String window : fp1.keySet()) {
            for (int pos1 : fp1.get(window)) {
                for (int pos2 : fp2.get(window)) {
                    // 扩展匹配范围
                    int start1 = pos1;
                    int start2 = pos2;
                    int length = WINDOW_SIZE;

                    // 向前扩展
                    while (start1 > 0 && start2 > 0 &&
                            text1.charAt(start1-1) == text2.charAt(start2-1)) {
                        start1--;
                        start2--;
                        length++;
                    }

                    // 向后扩展
                    while (start1 + length < text1.length() &&
                            start2 + length < text2.length() &&
                            text1.charAt(start1 + length) == text2.charAt(start2 + length)) {
                        length++;
                    }

                    if (length >= MIN_MATCH_LENGTH) {
                        String content = text1.substring(start1, start1 + length);
                        matches.add(new MatchFragment(
                                content,
                                start1,
                                start1 + length,
                                start2,
                                start2 + length
                        ));
                    }
                }
            }
        }

        // 去重处理
        return mergeOverlaps(matches);
    }

    // 合并逻辑优化（防止索引越界）
    private  List<MatchFragment> mergeOverlaps(List<MatchFragment> fragments) {
        fragments.sort(Comparator.comparingInt(MatchFragment::getStart1));
        List<MatchFragment> merged = new ArrayList<>();

        if (fragments.isEmpty()) return merged;

        MatchFragment current = fragments.get(0);
        for (int i = 1; i < fragments.size(); i++) {
            MatchFragment frag = fragments.get(i);

            // 判断是否可合并（允许部分重叠或相邻）
            if (frag.getStart1() <= current.getEnd1() + 1) { // +1允许相邻
                int overlap = current.getEnd1() - frag.getStart1();
                String newContent;
                if (overlap > 0) {
                    newContent = current.getContent() +
                            frag.getContent().substring(Math.min(overlap, frag.getContent().length()));
                } else {
                    newContent = current.getContent() + frag.getContent();
                }

                current = new MatchFragment(
                        newContent,
                        Math.min(current.getStart1(), frag.getStart1()),
                        Math.max(current.getEnd1(), frag.getEnd1()),
                        Math.min(current.getStart2(), frag.getStart2()),
                        Math.max(current.getEnd2(), frag.getEnd2())
                );
            } else {
                merged.add(current);
                current = frag;
            }
        }
        merged.add(current);
        return merged;
    }




    // 相似度计算方法（防止超过100%）
    private  double calculateSimilarity(String text1, String text2,
                                              List<MatchFragment> matches) {
        int totalMatch = matches.stream()
                .mapToInt(f -> f.getContent().length())
                .sum();

        // 取较长文档的长度作为分母
        int maxLength = Math.max(text1.length(), text2.length());
        return maxLength == 0 ? 0 : (double) totalMatch / maxLength;
    }


    /**
     * 查询作业
     * 
     * @param id 作业主键
     * @return 作业
     */
    @Override
    public BizWork selectBizWorkById(Long id)
    {
        return bizWorkMapper.selectBizWorkById(id);
    }

    /**
     * 查询作业列表
     * 
     * @param bizWork 作业
     * @return 作业
     */
    @Override
    public List<BizWork> selectBizWorkList(BizWork bizWork)
    {
        Long userId = SecurityUtils.getUserId();
        return bizWorkMapper.selectBizWorkList(bizWork.getTitle(),userId);
    }



    /**
     * 新增作业
     * 
     * @param bizWork 作业
     * @return 结果
     */
    @Override
    public int insertBizWork(BizWork bizWork)
    {
        /*
        * 判断当前教师是否存在课程
        * */
        if (bizWork.getCourseId() == null){
            throw new RuntimeException("请选择课程");
        }
        bizWork.setCreateTime(DateUtils.getNowDate());
        bizWork.setTeacherId(SecurityUtils.getUserId());
        return bizWorkMapper.insertBizWork(bizWork);
    }

    /**
     * 修改作业
     * 
     * @param bizWork 作业
     * @return 结果
     */
    @Override
    public int updateBizWork(BizWork bizWork)
    {
        bizWork.setUpdateTime(DateUtils.getNowDate());
        return bizWorkMapper.updateBizWork(bizWork);
    }

    /**
     * 批量删除作业
     * 
     * @param ids 需要删除的作业主键
     * @return 结果
     */
    @Override
    public int deleteBizWorkByIds(Long[] ids)
    {
        return bizWorkMapper.deleteBizWorkByIds(ids);
    }

    /**
     * 删除作业信息
     * 
     * @param id 作业主键
     * @return 结果
     */
    @Override
    public int deleteBizWorkById(Long id)
    {
        return bizWorkMapper.deleteBizWorkById(id);
    }
}
